/* 
 * File:   Library.cpp
 * Author: Lucky
 * 
 * Created on 22. září 2011, 10:33
 */

#define INT_MAX 100000

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <iostream>
#include <fstream>
#include "Library.h"
#include "Field.h"
#include "FieldSolver.h"
#include <mpi.h>

using namespace std;

Library::Library() {
}

Library::Library(const Library& orig) {
}

Library::~Library() {
}

int Library::LoadInt(const char* string, int min, int max) {
    int number;
    bool filled = false;
    do {
        if (filled) {
            std::cout << "Spatne zadani!" << std::endl;
            if (!std::cin.good()) {
                std::cin.clear();
                std::cin.ignore(INT_MAX, '\n');
            }
        }
        std::cout << string;
        std::cin >> number;
        std::cin.ignore(INT_MAX, '\n');
        filled = true;
    } while (!std::cin.good() || number < min || number > max);
    return number;
}

int* Library::LoadCoordinates(int count, int m, int n) {
    ///Coordinate** res = new Coordinate*[count];
    int* res = new int[2 * count];

    for (int i = 0; i < (2 * count); i += 2) {
        std::stringstream stream1;
        stream1 << "Souradnice " << (i + 2) / 2 << ", X osa: ";
        std::string str(stream1.str());
        int x = Library::LoadInt(str.c_str(), 0, m - 1);

        std::stringstream stream2;
        stream2 << "Souradnice " << (i + 2) / 2 << ", Y osa: ";
        str = std::string(stream2.str());
        int y = Library::LoadInt(str.c_str(), 0, n - 1);
        res[i] = x;
        res[i + 1] = y;
    }
    return res;
}

void Library::getNextCoordinate(int x, int y, int width, int * nextX, int * nextY) {
    //  cout << "Rx ry "<<a<<" "<<b << endl;
    if (x + 1 >= width) {
        x = 0;
        ++y;
    } else {
        ++x;
    }
    *nextX = x;
    *nextY = y;
}

int Library::getCoordLength(int width, int x, int y) {
    return y * width + x;

}

int* Library::LoadDataFromFile(char* fileName, int * width, int * height, int *q) {
    string line; //this will contain the data read from the file
    ifstream myfile(fileName); //opening the file.
    int* res;

    if (myfile.is_open()) //if the file is open
    {
        int charPos;

        //nacteni sirky
        getline(myfile, line);
        charPos = line.find(':');
        istringstream isa(line.substr(charPos + 1));
        isa >> *width;
        //nacteni vysky
        getline(myfile, line);
        charPos = line.find(':');
        istringstream isb(line.substr(charPos + 1));
        isb >> *height;
        //nacteni pocetSouradnic
        getline(myfile, line);
        charPos = line.find(':');
        istringstream isc(line.substr(charPos + 1));
        isc >> *q;
        cout << "Sirka " << *width << " vyska: " << *height << " pocet souradnic: " << *q << endl;
        //vytvoreni pole
        res = new int[ 2 * (*q)];
        cout << 2 * (*q) << endl;
        getline(myfile, line);
        int counter = 0, x, y;
        cout<<"Zadane souradnice: "<<endl<<flush;
        for (int i = 0; i <= line.size() && i <= 4 * (*q); i++) {
            char c = line[i];
            char a[1];
            // cout <<"Pruchod "<<endl;
            if (c == 'x') {
                //  cout <<"Nalezeno "<<endl;
                //                istringstream ix((line[i-1]));
                //                ix>>x;
                a[0] = line[i - 1];
                res[counter] = atoi(a);
                //                 istringstream iy((line[i+1]));
                //                iy>>y;
                counter++;
                a[0] = line[i + 1];
                res[counter] = atoi(a);
                counter++;
                 cout << res[counter-2]<<"x"<< res[counter-1] << endl<<flush;

            }
        }

        cout << "Velikost pole " << counter << endl;


    }
    myfile.close();
    return res;
}

//void Library::DestroyCoordinates(Coordinate** coords, int count) {
//    for (int i = 0; i < count; i++) {
//        delete coords[i];
//    }
//    delete coords;
//}

int Library::getXCoord(int width, int fieldNumber) {
    return fieldNumber % width;
}

int Library::getYCoord(int width, int fieldNumber) {
    return fieldNumber / width;
}

bool Library::cutStack(Field* source, Field* dest) {
    int newEnd = source->m_end + 1;
    while (source->m_field[newEnd] == true) {
        ++newEnd;
    }

    //Pokud je ve stacku uz jen 12 polozek k projeti (malo prace), tak nerozdelujeme
    if (newEnd + 12 > source->m_length) {
        return false;
    } else { //Pokud je ve stacku prace dost, rozdelime ho
        source->m_end = newEnd;
        dest->m_end = newEnd;
        for (int i = 0; i < newEnd; i++) {
            dest->setState(i, source->m_field[i]);
        }
        dest->setState(newEnd, true);

        return true;
    }

}

void Library::packStack(Field* sendedStack, char** buffer, int* packSize) {
    cout << "pack stack " << endl;
    int length = 10000;
    cout << "pack stack length" << length << endl;
    (*buffer) = new char[length];
    *packSize = 0;

    int end = sendedStack->m_end;
    int switchCount = sendedStack->m_switchCount;
    cout << "sended m_switchCount " << switchCount << endl;
    cout << "sended m_end " << end << endl << flush;

    MPI_Pack(&end, 1, MPI_INT, (*buffer), length, packSize, MPI_COMM_WORLD);
    MPI_Pack(&switchCount, 1, MPI_INT, (*buffer), length, packSize, MPI_COMM_WORLD);
    //    MPI_Pack(sendedStack->m_field, sendedStack->m_length, MPI_INT, (*buffer), length, packSize, MPI_COMM_WORLD);
    for (int i = 0; i < sendedStack->m_length; i++) {
        bool state = sendedStack->m_field[i];
        MPI_Pack(&state, 1, MPI_INT, (*buffer), length, packSize, MPI_COMM_WORLD);
    }

//    int position = 0;
//    MPI_Unpack((*buffer), (*packSize), &position, &end, 1, MPI_INT, MPI_COMM_WORLD);
//    MPI_Unpack((*buffer), (*packSize), &position, &switchCount, 1, MPI_INT, MPI_COMM_WORLD);
//
//    cout << "sended and unpacked m_switchCount " << switchCount << endl;
//    cout << "sended and unpacked m_end " << end << endl << flush;
}

void Library::unpackStack(Field** stack, char* sendedBuffer, int packSize, int fieldLength) {
    cout << "unpack stack packsize " << packSize << " fieldLength " << fieldLength << endl;
    (*stack) = new Field(fieldLength);
    int position = 0;

    int end = 0;
    int switchCount = 0;
    MPI_Unpack(sendedBuffer, packSize, &position, &end, 1, MPI_INT, MPI_COMM_WORLD);
    MPI_Unpack(sendedBuffer, packSize, &position, &switchCount, 1, MPI_INT, MPI_COMM_WORLD);
    for (int i = 0; i < fieldLength; i++) {
        bool state = false;
        MPI_Unpack(sendedBuffer, packSize, &position, &state, 1, MPI_INT, MPI_COMM_WORLD);
        (*stack)->setState(i, state);
    }
    //    MPI_Unpack(sendedBuffer, packSize, &position, (*stack)->m_field, fieldLength, MPI_INT, MPI_COMM_WORLD);

    cout << "unpacked stack packsize " << packSize << " fieldLength " << fieldLength << endl;
    cout << "m_switchCount " << switchCount << endl;
    cout << "m_end " << end << endl;

    (*stack)->m_end = end;
    (*stack)->m_switchCount = switchCount;
}

void Library::sendStack(Field* stack, int destP) {
    char* bfr;
    int pSize = 0;
    Library::packStack(stack, &bfr, &pSize);

    //    cout << "P" << my_rank << " posila packStack(" << pSize << ") procesoru " << (i+1) << endl << flush;
    MPI_Send(&pSize, 1, MPI_INT, destP, TAG_SENDWORK, MPI_COMM_WORLD);
    //    cout << "P" << my_rank << " posila stack delky " << tempStack->m_length << " procesoru " << (i+1) << endl << flush;
    MPI_Send(bfr, pSize, MPI_PACKED, destP, TAG_SENDWORK, MPI_COMM_WORLD);
    
    int end = 0;
    int switchCount = 0;
    int position = 0;
    MPI_Unpack(bfr, pSize, &position, &end, 1, MPI_INT, MPI_COMM_WORLD);
    MPI_Unpack(bfr, pSize, &position, &switchCount, 1, MPI_INT, MPI_COMM_WORLD);

    cout << "sended and unpacked m_switchCount " << switchCount << endl;
    cout << "sended and unpacked m_end " << end << endl << flush;
    delete bfr;
    delete stack;
}

void Library::sendNoWork(int destP) {
    int msg = 1;
    MPI_Send(&msg, 1, MPI_INT, destP, TAG_NOWORK, MPI_COMM_WORLD);
}

bool Library::receiveStack(int sourceP, Field** field, int stackLength) {
    MPI_Status status;
    int pSize = 0;
    MPI_Recv(&pSize, 1, MPI_INT, sourceP, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
    if (status.MPI_TAG == TAG_SENDWORK) { //Byla nam poslana prace
        if (pSize <= 0) {
            cerr << "pSize je mensi nez nula!!!" << endl << flush;
        }
        char* bfr = new char[pSize];
        MPI_Recv(bfr, pSize, MPI_PACKED, sourceP, TAG_SENDWORK, MPI_COMM_WORLD, &status);
        
        Library::unpackStack(field, bfr, pSize, stackLength);
        delete bfr;
        return true;
    } else { //Nebyla nam poslana prace
        return false;
    }
}

void Library::clickChessboard(Chessboard* board, Field* field) {
    int width = board->getWidth();
    for (int i = 0; i < field->m_length; i++) {
        if (field->m_field[i]) {
            int x = Library::getXCoord(width, i);
            int y = Library::getYCoord(width, i);
            board->pressLight(x, y);
        }
    }
}
